package com.java;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * NIO核心三要素：
 * 1. Channel
 * NIO的通道（Channel）可读可写
 * <p>
 * 2. Buffer
 * 操作堆外内存，可减少full GC
 * <p>
 * 3.Selector
 */
public class NIOServer {

    private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5,10,5, TimeUnit.SECONDS,new LinkedBlockingDeque<>(3));
    private static ServerSocketChannel serverSocketChannel;
    private static Selector selector;

    public static void main(String[] args) throws IOException {
        serverSocketChannel = ServerSocketChannel.open();
        // JVM和服务器间的一个连接绑定
        serverSocketChannel.bind(new InetSocketAddress(8080));
        // 配置为NIO非阻塞
        serverSocketChannel.configureBlocking(false);

        // Selector： 一线程处理多个连接
        // Socket：操作系统底层都有保留，都会存档，
        // 通过selector去选择我们需要的socket来操作
        selector = Selector.open();

        // 通过selector选择刚刚建立连接的socket
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 取结果数量，如果没有结果，则1秒后再次循环访问
            selector.select(1000L);
            Set<SelectionKey> selections = selector.selectedKeys();
            Iterator<SelectionKey> it = selections.iterator();

            while (it.hasNext()) {
                SelectionKey result = it.next();
                // 判断是否是新连接
                if (result.isAcceptable()) {
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    // 新连接，不理睬。等待数据传输，加上一些新的条件
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (result.isReadable()) {
                    // 结果有数据
                    SocketChannel channel = (SocketChannel) result.channel();
                    channel.configureBlocking(false);

                    // 告诉selector，目前正在处理中，不需要监听它了
                    result.cancel();

                    // TODO 创建线程处理连接...
                    threadPool.execute(new NioSocketProcessor(channel));
                }
            }
            // 清空
            selections.clear();
            selector.selectNow();
        }

    }

    static class NioSocketProcessor implements Runnable{

        private SocketChannel socketChannel;

        public NioSocketProcessor(SocketChannel socketChannel){
            this.socketChannel = socketChannel;
        }
        @Override
        public void run() {
            try {
                // HTTP协议解析
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                // 读取客户端请求内容
                socketChannel.read(byteBuffer);
                byteBuffer.flip();
                byte[] arr = byteBuffer.array();
                System.out.println(new String(arr));

                // 向客户端写
                String responseContent = "返回内容";
                ByteBuffer responseArr = ByteBuffer.wrap(responseContent.getBytes());
                socketChannel.write(responseArr);
                responseArr.clear();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 因为是长连接，不能关闭。
                try {
                    socketChannel.configureBlocking(false);
                    socketChannel.register(NIOServer.selector,SelectionKey.OP_READ);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
